package com.azt.back.interceptor;

import com.azt.api.pojo.Admin;
import com.azt.back.util.ContextUtils;
import com.azt.utils.IOUtils;
import com.azt.utils.IpUtil;
import com.xiaoleilu.hutool.util.StrUtil;
import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

/**
 * mybatis update 日志记录
 * Created by 张栋 on 2016/10/20 9:47m
 * http://git.oschina.net/baomidou/mybatis-plus/blob/master/mybatis-plus/src/main/java/com/baomidou/mybatisplus/plugins/SqlExplainInterceptor.java?dir=0&filepath=mybatis-plus%2Fsrc%2Fmain%2Fjava%2Fcom%2Fbaomidou%2Fmybatisplus%2Fplugins%2FSqlExplainInterceptor.java&oid=79d56fc67294710979872c56a0f4917f7b5ebb04&sha=b62cc640a8a3151dfa38dcf210e3694ba8994489
 */
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class MybatisLoggerInterceptor implements Interceptor {
    private static final Logger operation = LoggerFactory.getLogger("myOperation");
    private Properties props = null;
    /**
     * 发现执行全表 delete update 语句是否停止执行
     */
    private boolean stopProceed = true;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        /**
         * 处理 DELETE UPDATE 语句
         */
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1];
        //保存日志
        saveLog(ms, parameter);
        return invocation.proceed();
    }

    private void saveLog(MappedStatement mappedStatement, Object obj) throws Exception {

            BoundSql boundSql = mappedStatement.getBoundSql(obj);
            Admin admin = null;
            Integer userid = 0;
            String ipAddr =null;
            boolean needExplain = true;
            try {
                try{
                    admin = ContextUtils.getAdmin();
                    ipAddr = IpUtil.getIpAddr(ContextUtils.getRequest());
                }catch (Exception e){
                    ipAddr="localhost";
                }
            if (admin != null) {
                userid = admin.getId();
            }

            SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
            String param = com.alibaba.fastjson.JSONObject.toJSONString(obj);

            switch (sqlCommandType) {
                case INSERT:
                    operation.trace("ip:{},adminid:{},新增{},\r\n 参数:{},\r\n  sql:{}", ipAddr, userid,obj!=null? obj.getClass().getSimpleName():"", param, boundSql.getSql());
                    needExplain = false;
                    break;
                case UPDATE:
					operation.trace("ip:{},adminid:{},更新{},\r\n 参数:{},\r\n  sql:{}", ipAddr, userid, obj!=null?obj.getClass().getSimpleName():"", param, boundSql.getSql());
                   
                    break;
                case DELETE:
					operation.trace("ip:{},adminid:{},删除{},\r\n 参数:{},\r\n  sql:{}", ipAddr, userid, obj!=null?obj.getClass().getSimpleName():"", param, boundSql.getSql());
                	
                    break;
            }
            }catch(Exception e ){
                e.printStackTrace();
            }

			if (needExplain) {
			    Configuration configuration = mappedStatement.getConfiguration();
			    Connection connection = configuration.getEnvironment().getDataSource().getConnection();
			    sqlExplain(configuration, mappedStatement, boundSql, connection, obj);
			}

    }


    @SuppressWarnings("resource")
    protected void sqlExplain(Configuration configuration, MappedStatement mappedStatement, BoundSql boundSql,
                              Connection connection, Object parameter) throws Exception {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            StringBuilder explain = new StringBuilder("EXPLAIN ");
            explain.append(boundSql.getSql());
            String sqlExplain = explain.toString();
            StaticSqlSource sqlsource = new StaticSqlSource(configuration, sqlExplain, boundSql.getParameterMappings());
            MappedStatement.Builder builder = new MappedStatement.Builder(configuration, "explain_sql", sqlsource,
                    SqlCommandType.SELECT);
            builder.resultMaps(mappedStatement.getResultMaps()).resultSetType(mappedStatement.getResultSetType())
                    .statementType(mappedStatement.getStatementType());
            MappedStatement query_statement = builder.build();
            DefaultParameterHandler handler = new DefaultParameterHandler(query_statement, parameter, boundSql);
            stmt = connection.prepareStatement(sqlExplain);
            handler.setParameters(stmt);
            rs = stmt.executeQuery();
            while (rs.next()) {
                if(!StrUtil.containsIgnoreCase(rs.getString("Extra"), "Using where")) {
                    String tip = " Full table operation is prohibited. SQL: " + boundSql.getSql();
                    if (this.isStopProceed()) {
                        throw new Exception(tip);
                    }
                    operation.error(tip);
                    break;
                }
            }

        }  finally {
            IOUtils.closeQuietly(rs, stmt, connection);
        }
    }

    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }


    public void setProperties(Properties properties) {
        this.props = properties;
    }

    public boolean isStopProceed() {
        return stopProceed;
    }

    public void setStopProceed(boolean stopProceed) {
        this.stopProceed = stopProceed;
    }


}
